﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace GZSolver
{
  /// <summary>
  /// Main class for solving linear system
  /// </summary>
  public class SequentialGaussZeidelCalculator : LinearSystemCalculator
  {
    /// <summary>
    /// Solves linear system using coefficients matrix A and coefficients vector B.
    /// </summary>
    /// <param name="coeffs">Matrix A</param>
    /// <param name="b">Vector B</param>
    /// <param name="epsilon">Sets solving accuracy</param>
    /// <returns>Solving results.</returns>
    public override LinearSystemSolvingResults SolveLinearSystem(Matrix coeffs, double[] b, double epsilon)
    {
      LinearSystemSolvingResults result = new LinearSystemSolvingResults();
      double[] resultVector = new double[coeffs.RowCount];
      double[] tempResultVector = new double[coeffs.RowCount];
      int rectangleMatrixSize = coeffs.RowCount;
      double tempAccuracy = 0.0;
      int counter = 0, k = 0, l = 0;
      // Stopwatch for measurement algorithm performance
      Stopwatch stopWatch = new Stopwatch();
      stopWatch.Start();
      // Basic sequential algorithm
      do
      {
        counter++;
        tempAccuracy = 0;
        if (counter > 1000)
        {
            throw new Exception("The system couldn't be solved.");
        }
        for (k = 0; k < rectangleMatrixSize; k++)
        {
          tempResultVector[k] = resultVector[k];
          double var = 0;
          for (l = 0; l < rectangleMatrixSize; l++)
          {
            if (k != l)
            {
              var += coeffs[k][l] * resultVector[l];
            }
          }
          resultVector[k] = (b[k] - var) / coeffs[k][k];
          tempAccuracy += Math.Abs(tempResultVector[k] - resultVector[k]);
        }
      }
      while (tempAccuracy > epsilon);
      stopWatch.Stop();

      result.ResultVector = tempResultVector;
      result.SolvingTime = stopWatch.ElapsedMilliseconds;
      result.ElapsedTicks = stopWatch.ElapsedTicks;

      return result;
    }
  }
}
